#include "SocketServer.h"

SocketServer::SocketServer(void) {}

/*
 * Constructor for a socket server, opens up a socket to listen on
 * sets blocking or non blocking mode and listens
 */
SocketServer::SocketServer(int tcpPort, int connections)
{
    // initiate class variables
    std::vector<Socket *> clients(connections);
    clientIdCount = 1;
    full = false;
    maxConnections = connections;
    tcpListenPort = tcpPort;
    tcpListen = Socket(0,tcp);
    highSock = (unsigned int) tcpListen.s;

    // setup tcp listening port and bind
    memset(&tcpaddr,0,sizeof(tcpaddr));
    tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    tcpaddr.sin_family = AF_INET;
    tcpaddr.sin_port = htons(tcpListenPort);

    if(bind(tcpListen.s, (sockaddr *)&tcpaddr, sizeof(tcpaddr)) < 0) {
       fprintf(stderr, "Cannot bind tcp listening socket\n"); 
       disconnect();
       exit(1);
    }
    listen(tcpListen.s, connections);
    printf("Listening for TCP connections on port %i ...\n", tcpListenPort);

    // setup udp port and bind
    /*memset(&udpaddr,0,sizeof(udpaddr));
    udpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    udpaddr.sin_family = AF_INET;
    udpaddr.sin_port = htons(udpListenPort);

    if(bind(udpListen.s, (sockaddr *)&udpaddr, sizeof(udpaddr)) < 0) {
        disconnect();
        fprintf(stderr, "Cannot bind udp listening socket");
        exit(1);
    }
    listen(udpListen.s, connections);
    printf("Waiting for UDP communication on port %i ...\n", udpListenPort);*/
}

/*
 * Destructor
 */
SocketServer::~SocketServer()
{
}

/*
 * Method to accept socket s
 * could add to a list of clients?
 */
int SocketServer::acceptConn(Socket *socket)
{
    if(clients.size()+1 > (unsigned)maxConnections) {
        full = true;
        printf("Have reached client connection capacity\n");
        return 0;
    }
    socklen_t clientAddrLen;
	sockaddr_in clientAddr;
    clientAddrLen = sizeof(clientAddr);
    SOCKET newsocket = accept(socket->s, (sockaddr *) &clientAddr, &clientAddrLen);
    if(newsocket < 0) {
        fprintf(stderr,"Cannot accept client connection\n");
        perror("accept");
        return 0;
        /*int rc = WSAGetLastError();
        if(rc==WSAEWOULDBLOCK)return 0; // non-blocking call, no request pending
        else throw "Invalid socket";*/
    }
    Socket *r = new Socket(clientIdCount, socket->type, newsocket);
    clients.push_back(r);
    printf("connected to client %i, host: %s, port: %i\n",clientIdCount, 
       inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
    clientIdCount++;
    return 1;
}

/*
 * Read the sockets to check for data and new connections
 */
int SocketServer::readSockets()
{
    FD_ZERO(&readfds);
    // add all of the client fds
    for(int i=0; i<(int)clients.size(); i++)
    {
        if(clients[i]->s==0) continue;
        FD_SET(clients[i]->s, &readfds);
        if(clients[i]->s > highSock)
            highSock = (unsigned int)clients[i]->s;
    }
    // add the listening socket
    FD_SET(tcpListen.s, &readfds);

    struct timeval timeout;
    timeout.tv_sec  = 3;
    timeout.tv_usec = 0;
    int errno;
    
    // select the largest value
    int sel = select(highSock+1, &readfds, NULL, NULL, &timeout);
    if(sel==-1) {
       if (errno==EINTR || errno==0) {
          readSockets();
       }
       if(errno!=0) {
          perror("select");
          fprintf(stderr, "Could not read sockets\n");
       }
    }
    // check the listen sockets for new connections
    if(FD_ISSET(tcpListen.s,&readfds) && !full)
       acceptConn(&tcpListen);
    // otherwise return number of sockets read or 0 for timeout
    return sel;
}

/*
 * Test to see if a socket is readable using select()
 */
bool SocketServer::readable(Socket const * const s)
{
    if(FD_ISSET(s->s,&readfds))
        return true;
    return false;
}

/*
 * Delete a client by id
 */
void SocketServer::deleteClient(int id)
{
    for(unsigned int i=0; i<clients.size(); i++)
    {
        if(clients[i]->id == id) {
            delete clients[i];
            clients.erase(clients.begin()+i);
            full = false;
            printf("Client %i disconnected\n",id);
            return;
        }
    }
    printf("Could not remove client reference\n");
}

/*
 * Handle a receive error
 */
void SocketServer::receiveError(int r, Socket *client)
{
    int errno;
    if(client->type==tcp) {
      deleteClient(client->id);
    }
    if(errno!=0) {
       perror("recv");
       printf("Error reading from socket\n");
    }
}

/*
 * Handle a sending error
 */
void SocketServer::sendError(int r, Socket *client)
{
   int errno;
   if(client->type==tcp) {
      deleteClient(client->id);
   }
   if(errno!=0) {
      perror("send");
      printf("Error sending to client socket\n");
   }
}

